package com.unswift.utils;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.unswift.annotation.api.Api;
import com.unswift.annotation.api.ApiField;
import com.unswift.annotation.api.ApiMethod;
import com.unswift.exception.JsonException;

@Api(value="Json公共操作类")
public final class JsonUtils {

	@ApiField("Json格式的 关键词，主要在Json格式化时使用")
	public final static String JSON_SPLIT="{}:,\"/ \f\n\r\t";
	
	@ApiField("Json转换的本地线程对象，主要是初始化ObjectMapper对象")
	public static ThreadLocal<ObjectMapper> local = new ThreadLocal<ObjectMapper>() {
		protected ObjectMapper initialValue() {
			return new ObjectMapper();
		};
	};
	
	@ApiMethod(value="将java对象转为Json串", params=@ApiField("java对象"), returns=@ApiField("Json串，格式请百度Json格式"))
	public static String toJson(Object source) {
		try {
			return local.get().writeValueAsString(source);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("object.to.json.exception", e, e.getMessage());
		}
	}
	
	@ApiMethod(value="将Json串转为指定的java对象", params={@ApiField("Json传"), @ApiField("指定的java对象"), @ApiField("可变类类型，java对象存在泛型时可使用此参数")}, returns=@ApiField("指定Java对象"))
	public static <T> T toJava(String json, Class<T> target, Class<?>...elementClasses) {
		try {
			if(ObjectUtils.isEmpty(elementClasses)){
				return local.get().readValue(json, target);
			}else{
				return local.get().readValue(json, getCollectionType(local.get(), target, elementClasses));
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("json.to.object.exception", e, e.getMessage());
		}
	}
	
	@ApiMethod(value="组装Java泛型对象", params={@ApiField("当前线程的ObjectMapper对象"), @ApiField("目标Java对象"), @ApiField("泛型参数对象")})
	private static JavaType getCollectionType(ObjectMapper mapper, Class<?> targetClass, Class<?>... elementClasses) {
        return mapper.getTypeFactory().constructParametricType(targetClass, elementClasses);
    }
	
	@ApiMethod(value="是否时Json关键词", params=@ApiField("要判定的字符串"), returns=@ApiField("是否为Json关键词，true：是，false：否"))
	public static boolean isKeyword(String token){
		return ObjectUtils.asList("{","}",":","[","]").contains(token);
	}
	
	@ApiMethod(value="Json串格式化", params=@ApiField("Json串"), returns=@ApiField("格式化后的字符串"))
	public static String toJsonFormat(String source) {
		StringTokenizer tokens = new StringTokenizer(source, JSON_SPLIT, true);//拆分
		try {
			StringBuilder formatJson=new StringBuilder();
			String token;
			boolean first=true;
			while (tokens.hasMoreTokens()) {
				token=tokens.nextToken();
				if(toJsonFormatString(tokens, token, formatJson)){
					first=false;
					continue;
				}
				if(toJsonFormatMap(tokens, token, 0, true, formatJson)){
					first=false;
					continue;
				}
				if(toJsonFormatList(tokens, token, 0, true, formatJson)){
					first=false;
					continue;
				}
				if(toJsonFormatComment(tokens, token, 0, first, formatJson)){
					first=false;
					continue;
				}
				if(StringUtils.contains("\n,\r,\t", token, null)){
					first=false;
					continue;
				}
				formatJson.append(token);
				first=false;
			}
			return formatJson.toString();
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("json.format.exception", e, e.getMessage());
		}
	}
	
	@ApiMethod(value="如果被拆分后的Json当前索引以\"开始，则必须找到结束的\"", params={@ApiField("拆分后的Json"), @ApiField("当前索引的字符"), @ApiField("格式化后的字符缓存")}, returns=@ApiField("是否命中，true：命中，false：未命中"))
	private static boolean toJsonFormatString(StringTokenizer tokens, String token, StringBuilder formatJson) {
		String findToken;
		if ("\"".equals(token)) {
			if(tokens.hasMoreTokens()){
				do {
					findToken = tokens.nextToken();
					token += findToken;
				} while (token.endsWith("\\\"") || (!"\"".equals(findToken) && tokens.hasMoreTokens()));
			}
			formatJson.append(token);
			return true;
		}
		return false;
	}
	
	@ApiMethod(value="如果被拆分后的Json当前索引以{开始，则必须找到结束的}", params={@ApiField("拆分后的Json"), @ApiField("当前索引的字符"), @ApiField("当前的查找层次，默认未0，主要针对多各{的情况"), @ApiField("是否首次进入"), @ApiField("格式化后的字符缓存")}, returns=@ApiField("是否命中，true：命中，false：未命中"))
	private static boolean toJsonFormatMap(StringTokenizer tokens, String token, int level, boolean start, StringBuilder formatJson) {
		String findToken;
		if ("{".equals(token)) {
			StringBuilder subFormatJson=new StringBuilder();
			subFormatJson.append(start?toTab(level):"").append(token).append(FileUtils.nextLine());
			if(tokens.hasMoreTokens()){
				boolean paramFirst=true;
				do {
					findToken = tokens.nextToken();
					if(paramFirst){
						subFormatJson.append(toTab(level+1));
					}
					if(findToken.equals(",")){
						paramFirst=true;
					}else{
						paramFirst=false;
					}
					if(toJsonFormatString(tokens, findToken, subFormatJson)){
						continue;
					}
					if(toJsonFormatMap(tokens, findToken, level+1, false, subFormatJson)){
						continue;
					}
					if(toJsonFormatList(tokens, findToken, level+1, false, subFormatJson)){
						continue;
					}
					if(toJsonFormatComment(tokens, findToken, level+1, false, subFormatJson)){
						continue;
					}
					if(StringUtils.contains("\n,\r,\t", findToken, null)){
						continue;
					}
					if(findToken.equals("}")){
						if(!subFormatJson.toString().endsWith(FileUtils.nextLine())){
							subFormatJson.append(FileUtils.nextLine());
						}
						subFormatJson.append(toTab(level));
					}
					
					subFormatJson.append(findToken);
					if(findToken.equals(",")){
						subFormatJson.append(FileUtils.nextLine());
					}
				} while (!"}".equals(findToken) && tokens.hasMoreTokens());
			}
			String subFormat=subFormatJson.toString();
			String replace = subFormat.replace(FileUtils.nextLine(), "").replace("\t", "");
			if(replace.length()<50){
				formatJson.append(replace);
			}else{
				formatJson.append(subFormat);
			}
			return true;
		}
		return false;
	}
	
	@ApiMethod(value="如果被拆分后的Json当前索引以[开始，则必须找到结束的]", params={@ApiField("拆分后的Json"), @ApiField("当前索引的字符"), @ApiField("当前的查找层次，默认未0，主要针对多各[的情况"), @ApiField("是否首次进入"), @ApiField("格式化后的字符缓存")}, returns=@ApiField("是否命中，true：命中，false：未命中"))
	private static boolean toJsonFormatList(StringTokenizer tokens, String token, int level, boolean start, StringBuilder formatJson) {
		String findToken;
		if ("[".equals(token)) {
			StringBuilder subFormatJson=new StringBuilder();
			subFormatJson.append(start?toTab(level):"").append(token).append(FileUtils.nextLine());
			if(tokens.hasMoreTokens()){
				boolean paramFirst=true;
				do {
					findToken = tokens.nextToken();
					if(paramFirst){
						subFormatJson.append(toTab(level+1));
					}
					if(findToken.equals(",")){
						paramFirst=true;
					}else{
						paramFirst=false;
					}
					if(toJsonFormatString(tokens, findToken, subFormatJson)){
						continue;
					}
					if(toJsonFormatMap(tokens, findToken, level+1, false, subFormatJson)){
						continue;
					}
					if(toJsonFormatList(tokens, findToken, level+1, false, subFormatJson)){
						continue;
					}
					if(toJsonFormatComment(tokens, findToken, level+1, false, subFormatJson)){
						continue;
					}
					if(StringUtils.contains("\n,\r,\t", findToken, null)){
						continue;
					}
					if(findToken.equals("]")){
						if(!subFormatJson.toString().endsWith(FileUtils.nextLine())){
							subFormatJson.append(FileUtils.nextLine());
						}
						subFormatJson.append(toTab(level));
					}
					subFormatJson.append(findToken);
					if(findToken.equals(",")){
						subFormatJson.append(FileUtils.nextLine());
					}
				} while (!"]".equals(findToken) && tokens.hasMoreTokens());
			}
			String subFormat=subFormatJson.toString();
			String replace = subFormat.replace(FileUtils.nextLine(), "").replace("\t", "");
			if(replace.length()<50){
				formatJson.append(replace);
			}else{
				formatJson.append(subFormat);
			}
			return true;
		}
		return false;
	}
	
	@ApiMethod(value="如果被拆分后的Json当前索引以/开始，//为Json的注释", params={@ApiField("拆分后的Json"), @ApiField("当前索引的字符"), @ApiField("当前的查找层次，默认未0，主要针对多各[的情况"), @ApiField("是否首次进入"), @ApiField("格式化后的字符缓存")}, returns=@ApiField("是否命中，true：命中，false：未命中"))
	private static boolean toJsonFormatComment(StringTokenizer tokens, String token, int level, boolean first, StringBuilder formatJson){
		if("/".equals(token)){//-- 注释
			if(!tokens.hasMoreTokens()){
				formatJson.append(token);
				return false;
			}
			boolean afterNextLine=true;
			if(formatJson.length()>0 && !formatJson.toString().endsWith("\t") && !formatJson.toString().endsWith(FileUtils.nextLine())){
				formatJson.append(FileUtils.nextLine()).append(toTab(level));
				afterNextLine=false;
			}
			formatJson.append(token);
			String findToken;
			do {
				findToken = tokens.nextToken();
				token+=findToken;
				formatJson.append(findToken);
			} while (token.endsWith("\\\n") || (!"\n".equals(findToken) && tokens.hasMoreTokens()));
			if(afterNextLine){
				formatJson.append(toTab(level));
			}
			return true;
		}
		return false;
	}
	
	@ApiMethod(value="java对象转为Json并格式化", params=@ApiField("java对象"), returns=@ApiField("格式化后的Json串"))
	public static String toJsonFormat(Object source) {
		return toJsonFormat(source, 0, true, true);
	}
	
	@SuppressWarnings("unchecked")
	@ApiMethod(value="java对象转为Json并格式化", params={@ApiField("java对象"), @ApiField("格式化的层次，默认为0"), @ApiField("是否必须输出Tab"), @ApiField("是否首次")}, returns=@ApiField("格式化后的Json串"))
	private static String toJsonFormat(Object source, int level, boolean must, boolean start) {
		try {
			StringBuilder format=new StringBuilder();
			if(ObjectUtils.isEmpty(source)){
				return (must?toTab(level):"")+"null";
			}
			String json=toJson(source);
			if(source instanceof Map){
				if(json.length()<50){
					return json;
				}
				format.append(start?toTab(level):"").append("{").append(FileUtils.nextLine());
				Map<String, Object> sourceMap=(Map<String, Object>)source;
				Set<String> keys=sourceMap.keySet();
				int size=keys.size(),index=0;
				for (String key : keys) {
					format.append(toTab(level+1)).append("\"").append(key).append("\":");
					format.append(toJsonFormat(sourceMap.get(key), level+1, false, false));
					format.append(index<size-1?",":"").append(FileUtils.nextLine());
					index++;
				}
				format.append(toTab(level)).append("}");
				return format.toString();
			}else if(source instanceof List){
				if(json.length()<50){
					return json;
				}
				List<Object> list=(List<Object>)source;
				format.append(start?toTab(level):"").append("[").append(FileUtils.nextLine());
				int size=list.size(),index=0;
				for (Object item : list) {
					format.append(toJsonFormat(item, level+1, true, true));
					format.append(index<size-1?",":"").append(FileUtils.nextLine());
					index++;
				}
				format.append(toTab(level)).append("]");
				return format.toString();
			}else if(source.getClass().isArray()){
				if(json.length()<50){
					return json;
				}
				format.append(start?toTab(level):"").append("[").append(FileUtils.nextLine());
				int length=Array.getLength(source);
				for (int i = 0; i < length; i++) {
					format.append(toJsonFormat(Array.get(source, i), level+1, true, true));
					format.append(i<length-1?",":"").append(FileUtils.nextLine());
				}
				format.append(toTab(level)).append("]");
				return format.toString();
			}else if(source instanceof Comparable){
				return (start?toTab(level):"")+json;
			}else{
				if(json.length()<50){
					return json;
				}
				format.append(start?toTab(level):"").append("{").append(FileUtils.nextLine());
				List<Field> fieldList=ClassUtils.getFieldList(source.getClass());
				int size=fieldList.size(),index=0;
				for (Field field : fieldList) {
					format.append(toTab(level+1)).append("\"").append(field.getName()).append("\":");
					format.append(toJsonFormat(ClassUtils.get(source, field), level+1, false, false));
					format.append(index<size-1?",":"").append(FileUtils.nextLine());
					index++;
				}
				format.append(toTab(level)).append("}");
				return format.toString();
				
			}
		} catch (JsonException e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("json.format.exception", e, e.getMessage());
		}
	}
	
	@ApiMethod(value="根据Tab数量返回指定长度的Tab", params=@ApiField("tab长度"), returns=@ApiField("指定长度的Tab键"))
	private static String toTab(int tabNum){
		StringBuilder tab=new StringBuilder();
		for (int i = 0; i < tabNum; i++) {
			tab.append("\t");
		}
		return tab.toString();
	}
}
